added Feb 2001 SDK
[windows-sources.git] / shared source / wpf / src / host / dll / cookieshim.cxx
blob263a9a7c4e7cc4d3c999ff5d2b0159620c52e802
1 //+-----------------------------------------------------------------------
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // Description:
6 // [See .h]
7 //
8 // History:
9 // 2007/03/30 [....] Created
10 // 2007/09/20 [....] Ported Windows->DevDiv. See SourcesHistory.txt.
12 //------------------------------------------------------------------------
14 #include "precompiled.hxx"
15 #include "CookieShim.hxx"
16 #include "HostSupport.h"
17 #include "OleDocument.hxx"
18 #include "urlmoninterop.hxx"
19 #include "DllMain.hxx" // g_pOleDoc, g_mainThreadId
20 #include "..\Detours\Detours.h"
23 // CAutoSetWin32Error helps correctly set the last error from functions using objects with destructors.
24 // A dtor may do cleanup and thus call Win32 functions that overwrite what was previously set with
25 // SetLastError(). Declare the CAutoSetWin32Error instance before any other stack objects in a function.
26 // This way its dtor will be called after all others and will set the last Win32 error.
27 class CAutoSetWin32Error
29 DWORD m_error;
30 public:
31 CAutoSetWin32Error():
32 m_error(ERROR_SUCCESS) { }
33 void SetLastError(DWORD error)
35 m_error = error;
37 ~CAutoSetWin32Error()
39 // By convention, SetLastError() is called only when returning an error.
40 if(m_error != ERROR_SUCCESS)
42 ::SetLastError(m_error);
47 ////////////////////////////////////////////////////////////////////////////////////////
48 // CCookieShim
50 // WinInet must not be delay-loaded in order for these initializations to work correctly.
51 DWORD (WINAPI *CCookieShim::s_pfInternetSetCookieEx)(
52 LPCWSTR, __in_opt LPCWSTR, LPCWSTR, DWORD, __in_opt DWORD_PTR) = &InternetSetCookieExW;
53 BOOL (WINAPI *CCookieShim::s_pfInternetGetCookieEx)(
54 LPCWSTR, __in_opt LPCWSTR, __out_ecount_opt(*pcchData) LPWSTR, __inout LPDWORD pcchData, DWORD, LPVOID) = &InternetGetCookieExW;
56 bool CCookieShim::s_isApplicationThirdParty;
58 HRESULT CCookieShim::Init(bool isTopLevel, __in_opt LPCWSTR topLevelUri)
60 HRESULT hr = S_OK;
62 // Detours can be found on http://toolbox. It has a nice help file with it.
63 // It is enough to detour InternetSetCookieExW & InternetGetCookieExW because that's what the managed code
64 // and the WebOC use.
65 CHECK_ERROR_CODE(DetourTransactionBegin());
66 CHECK_ERROR_CODE(DetourUpdateThread(GetCurrentThread()));
67 CHECK_ERROR_CODE(DetourAttach((void**)&s_pfInternetSetCookieEx, InternetSetCookieExDetour));
68 CHECK_ERROR_CODE(DetourAttach((void**)&s_pfInternetGetCookieEx, InternetGetCookieExDetour));
69 CHECK_ERROR_CODE(DetourTransactionCommit());
71 s_isApplicationThirdParty = false;
72 if(!isTopLevel)
74 if(!topLevelUri || !topLevelUri[0])
75 { // browser didn't tell us => probably crossing security zones
76 s_isApplicationThirdParty = true;
78 else
80 // Test whether the startup URI is 3rd-party with respect to the top-level URI.
81 s_isApplicationThirdParty = IsThirdPartyUri(topLevelUri) != S_FALSE;
85 Cleanup:
86 return hr;
89 void CCookieShim::Uninit()
91 // Undetouring on shutdown is needed specifically to avoid DevDiv bug 161831: WMNetMgr.dll (part of the
92 // Windows Media control) somehow manages to make an (asynchronous) call to InternetGetCookieEx() after
93 // PHDLL is unloaded.
94 if(s_pfInternetSetCookieEx != InternetSetCookieEx) // detoured?
96 if(DetourTransactionBegin() == NOERROR &&
97 DetourUpdateThread(GetCurrentThread()) == NOERROR &&
98 DetourDetach((void**)&s_pfInternetSetCookieEx, InternetSetCookieExDetour) == NOERROR &&
99 DetourDetach((void**)&s_pfInternetGetCookieEx, InternetGetCookieExDetour) == NOERROR)
101 DetourTransactionCommit();
103 else
105 ASSERT(false);
110 DWORD WINAPI CCookieShim::InternetSetCookieExDetour(
111 LPCWSTR lpszUrl, __in_opt LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, DWORD flags, __in_opt DWORD_PTR P3PHeader)
113 CAutoSetWin32Error autoError;
114 if(!(lpszUrl && *lpszUrl && lpszCookieData && *lpszCookieData))
116 autoError.SetLastError(ERROR_INVALID_PARAMETER);
117 return false;
119 if(flags & ~(INTERNET_COOKIE_THIRD_PARTY | INTERNET_COOKIE_EVALUATE_P3P))
121 ASSERT(0);
122 autoError.SetLastError(ERROR_INVALID_PARAMETER);
123 return false;
126 HRESULT hr;
127 bool isThirdParty = (flags & INTERNET_COOKIE_THIRD_PARTY) != 0;
128 if(!isThirdParty)
130 // 3rd-party status has to be forced in two situations:
131 // - WebOC hosted in an XBAP. The WebOC always thinks it's a top-level browser, but the XBAP as a whole
132 // may be 3rd party to the top-level document.
133 // - Media fetched from outside the site of origin. The managed CookieHandler doesn't pass the 3rd party
134 // bit, but it assumes that the call to InternetSetCookieEx will be intercepted here and the bit added
135 // if necessary.
136 hr = IsThirdPartyUri(lpszUrl);
137 if(FAILED(hr))
139 autoError.SetLastError(ERROR_GEN_FAILURE);
140 return false;
142 isThirdParty = hr == S_OK;
145 CComPtr<IHostBrowser> spHostBrowser;
146 if(g_pOleDoc->GetWebBrowserForCurrentThread(&spHostBrowser) != S_OK)
148 autoError.SetLastError(ERROR_VC_DISCONNECTED); // "The session was canceled."
149 return false;
151 hr = spHostBrowser->SetCookie(
152 lpszUrl, lpszCookieName, lpszCookieData, isThirdParty, reinterpret_cast<LPCWSTR>(P3PHeader));
153 if(SUCCEEDED(hr))
154 return true;
155 //! Assuming that the implementation of SetCookie returns E_ACCESSDENIED for a rejected cookie or
156 // otherwise HRESULT_FROM_WIN32.
157 if(hr == E_ACCESSDENIED)
158 return COOKIE_STATE_REJECT;
159 autoError.SetLastError(hr & 0xFFFF);
160 return false;
163 BOOL WINAPI CCookieShim::InternetGetCookieExDetour(
164 LPCWSTR pUri, __in_opt LPCWSTR pCookieName, __out_ecount_opt(*pcchData) LPWSTR pCookieData, __inout LPDWORD pcchData, DWORD flags, LPVOID)
166 CAutoSetWin32Error autoError;
167 if(!(pUri && *pUri && pcchData))
169 autoError.SetLastError(ERROR_INVALID_PARAMETER);
170 return false;
172 // pCookieData can be null on entry. Then only the size of the buffer needed for the cookie data is returned.
173 if(flags & ~INTERNET_COOKIE_THIRD_PARTY)
175 ASSERT(0);
176 autoError.SetLastError(ERROR_INVALID_PARAMETER);
177 return false;
180 HRESULT hr;
181 bool isThirdParty = (flags & INTERNET_COOKIE_THIRD_PARTY) != 0;
182 if(!isThirdParty)
184 //[See explanation in InternetSetCookieExDetour.]
185 hr = IsThirdPartyUri(pUri);
186 if(FAILED(hr))
188 autoError.SetLastError(ERROR_GEN_FAILURE);
189 return false;
191 isThirdParty = hr == S_OK;
194 CComPtr<IHostBrowser> spHostBrowser;
195 if(g_pOleDoc->GetWebBrowserForCurrentThread(&spHostBrowser) != S_OK)
197 autoError.SetLastError(ERROR_VC_DISCONNECTED); // "The session was canceled."
198 return false;
200 CComBSTR cookieData;
201 hr = spHostBrowser->GetCookie(pUri, pCookieName, isThirdParty, &cookieData);
202 if(FAILED(hr))
204 autoError.SetLastError(hr & 0xFFFF);
205 return false;
208 unsigned cchData = cookieData.Length()+1; // +1 for terminating '\0'
209 if(cchData <= 1 || cchData > CCH_COOKIE_MAX)
211 autoError.SetLastError(ERROR_INVALID_DATA);
212 return false;
214 // Make sure the terminating '\0' is not included in the BSTR's character count. (This is possible and
215 // valid in principle but unexpected in most cases.)
216 ASSERT(cookieData[cchData-2] && !cookieData[cchData-1]);
218 if(!pCookieData)
220 // Return space needed to store cookie data.
221 // *2: Kid you not, this is what WinINet returns! (inetcore\wininet\http\cookie.cxx)
222 // And only in this case (and when returning ERROR_INSUFFICIENT_BUFFER--like below). (!#$#$)
223 // And the ClickOnce SystemNetDownloader is aware of this doubling and allocates only half that many
224 // characters.
225 *pcchData = cchData*2;
227 else
229 if(*pcchData < cchData)
231 *pcchData = cchData*2; // see above...
232 autoError.SetLastError(ERROR_INSUFFICIENT_BUFFER);
233 return false;
235 StringCchCopy(pCookieData, *pcchData, cookieData);
236 *pcchData = cchData; // WinINet includes the final '\0' in the count.
238 return true;
241 HRESULT CCookieShim::GetInternetSecurityManagerForCurrentThread(__deref_out IInternetSecurityManager **ppSecMgr)
243 if(GetCurrentThreadId() == g_mainThreadId)
244 return UrlmonInterop::GetSecurityManager(ppSecMgr);
246 static IClassFactory *pSecMgrFactory;
247 if(!pSecMgrFactory)
249 HRESULT hr = CoGetClassObject(
250 CLSID_InternetSecurityManager, CLSCTX_INPROC_SERVER, 0,
251 IID_IClassFactory, (void**)&pSecMgrFactory);
252 if(FAILED(hr))
253 return hr;
255 return pSecMgrFactory->CreateInstance(0, IID_IInternetSecurityManager, (void**)ppSecMgr);
258 HRESULT CCookieShim::IsThirdPartyUri(LPCWSTR uri)
260 ASSERT(*uri);
261 if(s_isApplicationThirdParty)
262 return S_OK;
264 // Quick test for the startup URI
265 LPCWSTR startupUri = g_pOleDoc->GetStartupUri();
266 if(_wcsicmp(startupUri, uri) == 0)
267 return S_FALSE;
269 // The same method is used here as in IE: Get the "security id" of both URIs and then call
270 // CompareSecurityIds(). Despite its name, CompareSecurityIds() actually tests for matching
271 // "minimal domains" only. For example, "http://www.abc.com" and "http://images.abc.com" are considered
272 // matching. But "http://abc.com.uk" and "http://cnn.com.uk" are not.
273 HRESULT hr;
274 static UriSecurityId s_startupUriSId;
275 UriSecurityId sid2;
276 CComPtr<IInternetSecurityManager> spSecMgr;
277 CKHR(GetInternetSecurityManagerForCurrentThread(&spSecMgr));
278 if(!s_startupUriSId.id[0]) // init once, lazily
280 CKHR(s_startupUriSId.Set(startupUri, spSecMgr));
282 CKHR(sid2.Set(uri, spSecMgr));
283 CKHR(CompareSecurityIds(s_startupUriSId.id, s_startupUriSId.idLen, sid2.id, sid2.idLen, 0));
284 hr = hr == S_OK ? S_FALSE : S_OK;
285 Cleanup:
286 return hr;